#include "common.hpp"

static const char *optString = "h?";

static const struct option longOpts[] = {
	{ "help", no_argument, NULL, 'h' },
	{ 0,0,0,0}
};

int main (int argc, char* const argv[]) {

	ipc::shared_memory_with_exceptions<size_t> listas	( my_ftok(COUNTER|LISTAS));

	ipc::semaphore_with_exceptions mutex_listas ( my_ftok(MUTEX|LISTAS),0);

	ipc::semaphore_with_exceptions mutex_transportadoraA ( my_ftok(MUTEX|TRANSPORTADORA_A),0 );
	ipc::semaphore_vector mutex_llenadoras ( my_ftok(MUTEX|LLENADORA_0),MAX_LLENADORAS );
	ipc::semaphore_vector mutex_azucaradoras ( my_ftok(MUTEX|AZUCARADORA_0),MAX_LLENADORAS );
	ipc::semaphore_with_exceptions mutex_tapadora ( my_ftok(MUTEX|TAPADORA),0 );
	ipc::semaphore_with_exceptions mutex_etiquetadora ( my_ftok(MUTEX|ETIQUETADORA),0 );
	ipc::semaphore_with_exceptions mutex_repositor ( my_ftok(MUTEX|REPOSITOR),0 );

	ipc::messages_queue_with_exceptions transportadoraA_salida ( my_ftok(MSG_QUEUE|TRANSPORTADORA_A) );
	ipc::messages_queue_with_exceptions llenadoras_salida ( my_ftok(MSG_QUEUE|LLENADORA_0) );
	ipc::messages_queue_with_exceptions tapadora_salida ( my_ftok(MSG_QUEUE|TAPADORA) );
//	ipc::messages_queue_with_exceptions etiquetadora_salida ( my_ftok(MSG_QUEUE|ETIQUETADORA) );
	ipc::messages_queue_with_exceptions repositor_salida ( my_ftok(MSG_QUEUE|REPOSITOR) );
	ipc::messages_queue_with_exceptions canal_sync ( my_ftok(MSG_QUEUE|SYNC) );

	int longIndex = 0;
	int option = 0;
	bool ayuda = false;
	char orden;
	
	while ( (option = getopt_long(argc,argv,optString,longOpts,&longIndex) ) != EOF ) {
		switch (option) {
			case '?':
			case 'h':
			case ':':
				ayuda = true;
				break;
			default:
				break;
		}
	}

	if ( ayuda ) {
		std::cerr << "Proceso para preparación del entorno y ejecución." << std::endl;
		std::cerr << "\tUso: " << argv[0] << " [-h] [-r natural] [-p natural]" << std::endl;
		std::cerr << "\tParametros:" << std::endl;
		std::cerr << "\t\t-h,--help:\tMostrar este mensaje de ayuda." << std::endl;

		return (ayuda) ? 0 : -1;
	}

	logger.setColor("\033[1;32m");
	try {
		mutex_listas.get	 (PERMITS | IPC_CREAT|IPC_EXCL);
		listas.get		 (PERMITS | IPC_CREAT|IPC_EXCL);
		*listas = 0;

//		mutex_sync.get		 (PERMITS | IPC_CREAT|IPC_EXCL);
		canal_sync.get		 (PERMITS | IPC_CREAT|IPC_EXCL);
		sToken token;
		token.esperando = 0;
		// Agrego un token a al canal de sincronización por cada grupo llenadora-azucaradora.
		for (size_t i=0; i<MAX_LLENADORAS; ++i) {
			token.tipo = i+1;
			canal_sync.send(token);
		}

		mutex_transportadoraA.get(PERMITS | IPC_CREAT|IPC_EXCL);
		ipc::assert ( mutex_llenadoras.get	 (PERMITS | IPC_CREAT|IPC_EXCL) == 0, "get", mutex_llenadoras );
		ipc::assert ( mutex_azucaradoras.get	 (PERMITS | IPC_CREAT|IPC_EXCL) == 0, "get", mutex_azucaradoras );
		mutex_tapadora.get(PERMITS | IPC_CREAT|IPC_EXCL);
		mutex_etiquetadora.get(PERMITS | IPC_CREAT|IPC_EXCL);
		mutex_repositor.get(PERMITS | IPC_CREAT|IPC_EXCL);

		// Cintas transportadoras
		llenadoras_salida.get(PERMITS | IPC_CREAT|IPC_EXCL);
		tapadora_salida.get(PERMITS | IPC_CREAT|IPC_EXCL);
//		etiquetadora_salida.get(PERMITS | IPC_CREAT|IPC_EXCL);
		transportadoraA_salida.get(PERMITS | IPC_CREAT|IPC_EXCL);
		repositor_salida.get(PERMITS | IPC_CREAT|IPC_EXCL);

		// Maquinas.
		mutex_transportadoraA.init(1);		
		for (size_t i=0; i<MAX_LLENADORAS; ++i) {
			ipc::assert ( mutex_llenadoras.init(1,i) == 0, "init", mutex_llenadoras );
			ipc::assert ( mutex_azucaradoras.init(1,i) == 0, "init", mutex_azucaradoras );
		}
		mutex_tapadora.init(1);
		mutex_etiquetadora.init(1);
		mutex_repositor.init(1);

		// Productos. Botellas en transito.
		mutex_listas.init(1);
//		mutex_sync.init(1);

		logger << "Lanzador: Objetos IPC creados." << nl;

		const char* vacias = "5"; // botellas vacias disponibles.
		const char* jugo = "1"; // dosis de jugo por llenadora. (si fueran 5 llenadoras y cada una tuviera 5 dosis inicialemente; 5x5 = 25)
		const char* tapas = "5"; // cantidad de tapas en la tapadora.
		const char* etiquetas = "5"; // cantidad de etiquetas en la etiquetadora.
		const char* pack = "5"; // cantidad de items en cada reposición.
		const char* azucar = "1"; // cantidad de azucar por azucaradora.

		// Activo la transportadora de botellas vacias (transportadoraA).
		if (fork() == 0)
			execlp("./transportadora_A","transportadora_A","-r",vacias,(char*)NULL);
	
		// Activo las llenadoras y azucaradoras.
		size_t llenadoras = (MAX_LLENADORAS > 5) ? 5 : MAX_LLENADORAS;
		for (size_t i=0; i<llenadoras; ++i) {
			if (fork() == 0)
				execlp("./llenadora","llenadora","-i",c_parse(i),"-r",jugo,(char*)NULL);

			if (fork() == 0)
				execlp("./azucaradora","azucaradora","-i",c_parse(i),"-r",azucar,(char*)NULL);
		}
		
		// Activo la tapadora.
		if (fork() == 0)
			execlp("./tapadora","tapadora","-r",tapas,(char*)NULL);

		// Activo la etiquetadora.
		if (fork() == 0)
			execlp("./etiquetadora","etiquetadora","-r",etiquetas,(char*)NULL);

		// Activo el repositor.
		if (fork() == 0)
			execlp("./repositor","repositor","-p",pack,"-b","25","-c","25","-l","25","-j","25","-s","25",(char*)NULL);

	} catch (ipc::exception& e) {
		error << "Lanzador: " << e.what() << "." << nl;
	}

	logger << "Lanzador: Ingrese 'd' para llamar a destruir y 'q' para salir." << nl;
	do
		std::cin >> orden;
	while ( (orden != 'q') && (orden != 'd') );

	if (orden == 'd') {
		pid_t destructor = fork();
		if (destructor == 0)
			execlp("./destructor","destructor",(char*)NULL);
		else
			waitpid(destructor,NULL,0);	
	}
	
	logger << "Lanzador: Fin." << nl;

	return 0;
}

